using System;
using System.Collections.Generic;
using SIP.PN.DAO.Interfaces;
using SIP.PN.Filtros.Interfaces;
using SIP.PN.Modelo.Entidades;
using SIP.PN.Servicios.Interfaces;
using SIP.PN.Servicios.Interfaces.DTO;
using SIP.PN.Servicios.Interfaces.Excepciones;

namespace SIP.PN.Servicios
{
    public class ServicioOperario : Servicio, IServicioOperario
    {
        #region Daos

        private IDaoEspecialidad daoEspecialidad;
        public IDaoEspecialidad DaoEspecialidad
        {
            get { return daoEspecialidad; }
            set { daoEspecialidad = value; }
        }

        private IDaoNivelEspecialidad daoNivelEspecialidad;
        public IDaoNivelEspecialidad DaoNivelEspecialidad
        {
            get { return daoNivelEspecialidad; }
            set { daoNivelEspecialidad = value; }
        }

        private IDaoTipoDeDocumento daoTipoDeDocumento;
        public IDaoTipoDeDocumento DaoTipoDeDocumento
        {
            get { return daoTipoDeDocumento; }
            set { daoTipoDeDocumento = value; }
        }

        private IDaoOperario daoOperario;
        public IDaoOperario DaoOperario
        {
            get { return daoOperario; }
            set { daoOperario = value; }
        }

        private IServicioAplicacion srvAplicacion;
        public IServicioAplicacion SrvAplicacion
        {
            get { return srvAplicacion; }
            set { srvAplicacion = value; }
        }

        #endregion

        #region Operario

        private Operario ObtenerOperario(string codigoTipoDeDocumento, string numeroDocumento)
        {
            TipoDeDocumento tipoDeDocumento = DaoTipoDeDocumento.Obtener(codigoTipoDeDocumento);
            if (tipoDeDocumento == null)
                throw new ExcepcionOtra("No existe el tipo de documento");
            return DaoOperario.Obtener(tipoDeDocumento, numeroDocumento);
        }

        public IList<OperarioDTO> Listar(string orden, int desde, int cantidad, string tipoDeDocumento,
                                      string numeroDocumento, string apellido, string nombre, string estado)
        {
            IFiltroOperario filtro = ObtenerFiltro<IFiltroOperario>();
            if (!string.IsNullOrEmpty(tipoDeDocumento))
                filtro.TipoDeDocumento = DaoTipoDeDocumento.Obtener(tipoDeDocumento);
            filtro.NumeroDocumento = numeroDocumento;
            filtro.Apellido = apellido;
            filtro.Nombre = nombre;
            if (!string.IsNullOrEmpty(estado))
                filtro.Activo = estado == "S";
            filtro.Cantidad = cantidad;
            filtro.Desde = desde;

            if (!string.IsNullOrEmpty(orden))
            {
                if (orden.EndsWith(" DESC"))
                {
                    filtro.AgregarOrden(orden.Substring(0, orden.Length - 5), false);
                }
                else
                {
                    filtro.AgregarOrden(orden, true);
                }
            }
            IList<OperarioDTO> r = new List<OperarioDTO>();
            foreach (Operario item in DaoOperario.Listar(filtro))
            {
                r.Add(ConvertToDTO(item));
            }
            return r;
        }

        public int ObtenerCantidadRegistros(string tipoDeDocumento, string numeroDocumento, string apellido, string nombre, string estado)
        {
            IFiltroOperario filtro = ObtenerFiltro<IFiltroOperario>();
            if (!string.IsNullOrEmpty(tipoDeDocumento))
                filtro.TipoDeDocumento = DaoTipoDeDocumento.Obtener(tipoDeDocumento);
            filtro.NumeroDocumento = numeroDocumento;
            filtro.Apellido = apellido;
            filtro.Nombre = nombre;
            if (!string.IsNullOrEmpty(estado))
                filtro.Activo = estado == "S";
            return DaoOperario.ObtenerCantidadRegistros(filtro);
        }

        public OperarioDTO Obtener(string tipoDeDocumento, string numeroDocumento)
        {
            TipoDeDocumento td = DaoTipoDeDocumento.Obtener(tipoDeDocumento);
            if (tipoDeDocumento == null)
                throw new ExcepcionOtra("No existe el tipo de documento");
            return ConvertToDTO(DaoOperario.Obtener(td, numeroDocumento));
        }

        public virtual void Crear(string codigoTipoDeDocumento, string numeroDocumento, string apellido, string nombre, DateTime fechaNacimiento, bool activo, string telefono, string celular, string localidad, string direccion)
        {
            TipoDeDocumento tipoDeDocumento = DaoTipoDeDocumento.Obtener(codigoTipoDeDocumento);
            if (tipoDeDocumento == null)
                throw new ExcepcionOtra("No existe el tipo de documento");

            if (Obtener(codigoTipoDeDocumento, numeroDocumento) != null)
                throw new ExcepcionCodigoUtilizado();

            Operario entidad = new Operario();
            entidad.TipoDeDocumento = tipoDeDocumento;
            entidad.NumeroDocumento = numeroDocumento;
            entidad.Apellido = apellido;
            entidad.Nombre = nombre;
            entidad.FechaNacimiento = fechaNacimiento;
            entidad.Activo = activo;
            entidad.Telefono = telefono;
            entidad.Celular = celular;
            entidad.Localidad = localidad;
            entidad.Direccion = direccion;
            DaoOperario.Guardar(entidad);
        }

        public virtual void Modificar(string codigoTipoDeDocumento, string numeroDocumento, string apellido, string nombre, DateTime fechaNacimiento, bool activo, string telefono, string celular, string localidad, string direccion)
        {
            TipoDeDocumento tipoDeDocumento = DaoTipoDeDocumento.Obtener(codigoTipoDeDocumento);
            if (tipoDeDocumento == null)
                throw new ExcepcionOtra("No existe el tipo de documento");

            Operario entidad = ObtenerOperario(codigoTipoDeDocumento, numeroDocumento);
            if (entidad == null)
                throw new ExcepcionOtra("No existe el operario.");

            entidad.TipoDeDocumento = tipoDeDocumento;
            entidad.NumeroDocumento = numeroDocumento;
            entidad.Apellido = apellido;
            entidad.Nombre = nombre;
            entidad.FechaNacimiento = fechaNacimiento;
            entidad.Activo = activo;
            entidad.Telefono = telefono;
            entidad.Celular = celular;
            entidad.Localidad = localidad;
            entidad.Direccion = direccion;
            DaoOperario.Guardar(entidad);
        }

        public virtual void Eliminar(string codigoTipoDeDocumento, string numeroDocumento)
        {
            Operario entidad = ObtenerOperario(codigoTipoDeDocumento, numeroDocumento);
            if (entidad == null)
                throw new ExcepcionOtra("No existe el operario.");
            DaoOperario.Eliminar(entidad);
        }

        #endregion

        #region Especialidad

        public IList<OperarioEspecialidadDTO> ListarOperarioEspecialidad(string codigoTipoDeDocumento, string numeroDocumento)
        {
            Operario entidad = ObtenerOperario(codigoTipoDeDocumento, numeroDocumento);
            if (entidad == null)
                throw new ExcepcionOtra("No existe el operario.");
            IList<OperarioEspecialidadDTO> r = new List<OperarioEspecialidadDTO>();
            foreach (OperarioEspecialidad item in entidad.Especialidades)
            {
                r.Add(ConvertToDTO(item));
            }
            return r;
        }

        public OperarioEspecialidadDTO ObtenerOperarioEspecialidad(string codigoTipoDeDocumento, string numeroDocumento, string codigoEspecialidad, string codigoNivelEspecialidad)
        {
            Operario entidad = ObtenerOperario(codigoTipoDeDocumento, numeroDocumento);
            if (entidad == null)
                throw new ExcepcionOtra("No existe el operario.");

            Especialidad especialidad = DaoEspecialidad.Obtener(codigoEspecialidad);
            if (especialidad == null)
                throw new ExcepcionOtra("No existe la especialidad.");

            NivelEspecialidad nivelEspecialidad = DaoNivelEspecialidad.Obtener(codigoNivelEspecialidad);
            if (nivelEspecialidad == null)
                throw new ExcepcionOtra("No existe el nivel especialidad.");

            OperarioEspecialidad op = entidad.ObtenerEspecialidad(especialidad, nivelEspecialidad);
            return ConvertToDTO(op);
        }

        public virtual void AgregarOperarioEspecialidad(string codigoTipoDeDocumento, string numeroDocumento, string codigoEspecialidad, string codigoNivelEspecialidad)
        {
            Operario entidad = ObtenerOperario(codigoTipoDeDocumento, numeroDocumento);
            if (entidad == null)
                throw new ExcepcionOtra("No existe el operario.");

            Especialidad especialidad = DaoEspecialidad.Obtener(codigoEspecialidad);
            if (especialidad == null)
                throw new ExcepcionOtra("No existe la especialidad.");
            
            NivelEspecialidad nivelEspecialidad = DaoNivelEspecialidad.Obtener(codigoNivelEspecialidad);
            if (nivelEspecialidad == null)
                throw new ExcepcionOtra("No existe el nivel especialidad.");

            entidad.AgregarEspecialidad(especialidad, nivelEspecialidad);
            DaoOperario.Guardar(entidad);
        }

        public virtual void ModificarOperarioEspecialidad(string codigoTipoDeDocumento, string numeroDocumento, string codigoEspecialidad, string codigoNivelEspecialidad, bool activo)
        {
            Operario entidad = ObtenerOperario(codigoTipoDeDocumento, numeroDocumento);
            if (entidad == null)
                throw new ExcepcionOtra("No existe el operario.");
            OperarioEspecialidad op = entidad.ObtenerEspecialidad(new Especialidad(codigoEspecialidad), new NivelEspecialidad(codigoNivelEspecialidad));
            if (op == null)
                throw new ExcepcionOtra("No existe la especialidad del operario.");
            op.Activo = activo;
            DaoOperario.Guardar(entidad);
        }

        public virtual void EliminarOperarioEspecialidad(string codigoTipoDeDocumento, string numeroDocumento, string codigoEspecialidad, string codigoNivelEspecialidad)
        {
            Operario entidad = ObtenerOperario(codigoTipoDeDocumento, numeroDocumento);
            if (entidad == null)
                throw new ExcepcionOtra("No existe el operario.");
            entidad.EliminarEspecialidad(new Especialidad(codigoEspecialidad), new NivelEspecialidad(codigoNivelEspecialidad));
            DaoOperario.Guardar(entidad);
        }

        #endregion

        #region Asignacion

        public IList<ProyectoTareaAsignacionDTO> ListarProyectoTareaAsignacion(DateTime fechaInicio, DateTime fechaFin, string apellido, string nombre, string especialidad, string nivel)
        {
            List<ProyectoTareaAsignacionDTO> data = new List<ProyectoTareaAsignacionDTO>();
            IList<OperarioEspecialidad> tmp = new List<OperarioEspecialidad>();
            IFiltroOperario filtro = ObtenerFiltro<IFiltroOperario>();
            filtro.Nombre = nombre;
            filtro.Apellido = apellido;
            filtro.Activo = true;

            IList<Operario> operarios = daoOperario.Listar(filtro);
            foreach (Operario operario in operarios)
            {
                foreach (OperarioEspecialidad operarioEspecialidad in operario.Especialidades)
                {
                    if (operarioEspecialidad.Activo && operarioEspecialidad.Especialidad.Codigo.Equals(especialidad))
                    {
                        if (string.IsNullOrEmpty(nivel) || operarioEspecialidad.NivelEspecialidad.Codigo.Equals(nivel))
                        {
                            if (!tmp.Contains(operarioEspecialidad))
                                tmp.Add(operarioEspecialidad);
                        }
                    }
                }
            }

            foreach (OperarioEspecialidad operarioEspecialidad in tmp)
            {
                if (CumpleFehasDeAsignacion(operarioEspecialidad.Operario, fechaInicio, fechaFin))
                {
                    ProyectoTareaAsignacionDTO dto = new ProyectoTareaAsignacionDTO();
                    dto.CodigoOperario = operarioEspecialidad.Operario.ToString();
                    dto.CodigoEspecialidad = operarioEspecialidad.Especialidad.Codigo;
                    dto.CodigoNivel = operarioEspecialidad.NivelEspecialidad.Codigo;
                    dto.Apellido = operarioEspecialidad.Operario.Apellido;
                    dto.Nombre = operarioEspecialidad.Operario.Nombre;
                    dto.Especialidad = operarioEspecialidad.Especialidad.Nombre;
                    dto.NivelEspecialidad = operarioEspecialidad.NivelEspecialidad.Nombre;
                    dto.FechaInicio = fechaInicio;
                    dto.FechaFin = fechaFin;
                    dto.Puntaje = operarioEspecialidad.Operario.CalcularEspecialidadPuntaje(operarioEspecialidad);
                    data.Add(dto);
                }
            }
            // Ordernar por evaluacion y seleccionar segun cantidad.
            data.Sort(new ProyectoTareaAsignacionDTOPuntajeSorter());
            return data;
        }

        public IList<OperarioAsignacionDTO> ListarOperarioAsignacion(string codigoTipoDeDocumento, string numeroDocumento)
        {
            List<OperarioAsignacionDTO> r = new List<OperarioAsignacionDTO>();
            Operario entidad = ObtenerOperario(codigoTipoDeDocumento, numeroDocumento);
            if (entidad == null)
                throw new ExcepcionOtra("No existe el operario.");
            
            foreach (Asignacion item in entidad.Asignaciones)
            {
                OperarioAsignacionDTO dto = new OperarioAsignacionDTO();
                r.Add(dto);

                dto.Codigo = item.Codigo.ToString();
                dto.Apellido = entidad.Apellido;
                dto.Nombre = entidad.Nombre;
                dto.Especialidad = item.OperarioEspecialidad.Especialidad.Nombre;
                dto.NivelEspecialidad = item.OperarioEspecialidad.NivelEspecialidad.Nombre;
                dto.Tarea = item.Tarea.Nombre;
                dto.FechaAsignacion = item.FechaAsignacion;
                dto.FechaInicio = item.Tarea.FechaInicio;
                dto.FechaFin = item.Tarea.FechaFinEsperado;
                if (item.Tarea.Estado.EsFinalizada() && item.Tarea.FechaFinReal.HasValue)
                    dto.FechaFin = item.Tarea.FechaFinReal.Value;
                dto.Evaluacion = item.CalcularEvaluacionFinal();
            }
            r.Sort(new OperarioAsignacionDTOFechaInicio());
            return r;
        }

        private static bool CumpleFehasDeAsignacion(Operario operario, DateTime fechaInicio, DateTime fechaFin)
        {
            IList<DateTime> fechas = operario.ObtenerAsignacionFechas(fechaInicio, fechaFin);
            if (fechas.Count!=2)
                return false;

            if (fechas[0].Date.Equals(fechaInicio.Date) && fechas[1].Date.Equals(fechaFin.Date))
                return true;
            return false;
        }

        #endregion
    }
}
